# -*- coding: utf-8 -*-
import json
import traceback
from xml.dom import minidom

from django.utils.encoding import smart_unicode

from common import orm
from common.console import db as console_user
from common.console.model import ACTION
from common.console.model import FORBIDDEN_ROLE
from common.utils import JsonResponse, EnhencedEncoder
from common.utils import track_logging
from common.utils.api import get_client_ip
from common.utils.exceptions import Error
from common.utils.exceptions import PermissionError, AuthenticateError
from common.utils.respcode import StatusCodeDict
from common.utils.tz import local_now

_LOGGER = track_logging.getLogger(__name__)


def get_perm_url(url_array):
    ''' 获取权限URL和资源ID '''
    if not url_array.endswith('/'):
        url_array += '/'
    url_array = url_array.replace('/fin/console/', '')
    if url_array.split('/')[-2].isdigit():
        perm_url = '/'.join(url_array.split('/')[0:-2]) + '/'
        return perm_url, url_array.split('/')[-2]
    else:
        perm_url = '/'.join(url_array.split('/')[0:-1]) + '/'
        return perm_url, None
    return None, None


def get_perm(url_array, action, role=0):
    perm_url, resource_id = get_perm_url(url_array)
    perm = console_user.get_perm_id(perm_url, action)
    if not role:
        return perm, resource_id
    if perm:
        role_data = console_user.get_role(role)
        if unicode(perm.id) in role_data.permissions.split(','):
            return perm.as_dict(), resource_id
    return False, resource_id


def ignore_perm_check(path):
    for ip in ['user/login/', 'bankcard/list/chargeable/']:
        if ip in path:
            return True
    return False


class UserMiddleware(object):
    """get user_id and token from header"""

    def process_request(self, req):
        user_id, token = req.META.get(
            'HTTP_X_AUTH_USER'), req.META.get('HTTP_X_AUTH_TOKEN')
        if not user_id:
            user_id, token = req.COOKIES.get(
                'fin_user_id'), req.COOKIES.get(
                'fin_user_token')
        if user_id and token:
            try:
                user_id = long(user_id)
            except ValueError:
                _LOGGER.error('user id format wrong!')
                req.user_id = None
                return
            if req.path.startswith('/fin/console'):
                info = console_user.get_online_info(user_id, token)
                if info:
                    req.user_id = user_id
                    user = console_user.get_user(user_id)
                    if user.role != FORBIDDEN_ROLE:
                        if ignore_perm_check(req.path):
                            req.user = user
                            return
                        url_array = req.path
                        perm, _ = get_perm(url_array, req.method, user.role)
                        if not perm:
                            return JsonResponse(dict(
                                status=PermissionError.STATUS,
                                msg=str('permission not enough')),
                                status=PermissionError.HTTPCODE)
                        else:
                            req.user = user
                            return
                    else:
                        return JsonResponse(dict(
                            status=PermissionError.STATUS,
                            msg=str("user is forbidden or not activited")),
                            status=PermissionError.HTTPCODE)
                else:
                    return JsonResponse(dict(
                        status=AuthenticateError.STATUS,
                        msg=str("token expired")),
                        status=AuthenticateError.HTTPCODE)
        req.user_id = req.user = None

    def process_response(self, req, resp):
        if not req.path.startswith('/fin/console'):
            return resp
        if req.method not in ACTION:
            return resp

        try:
            perm, resource_id = get_perm(req.path, req.method)
            if not perm:
                return resp
            content = json.loads(resp.content)
            status = content['status']
            if status != 0:
                return resp
            data = content['data']
            if resource_id is None:
                resource_id = data.get('order_id', None)
            if isinstance(data, dict):
                record = {
                    'resource': perm.desc,
                    'resource_id': resource_id,
                    'action': ACTION[req.method],
                    'content': req.body,
                    'operator': req.user_id
                }
                if req.path in (['/fin/console/user/login/', '/fin/console/user/']):
                    if req.user and req.user.nickname:
                        record['operator'] = req.user.nickname
                    else:
                        record['operator'] = 'login'
                    record['content'] = 'login'
                console_user.insert_record(record)
            elif isinstance(data, list):
                pass
                # for d in data:
                #     if 'id' not in d:
                #         continue
                #     else:
                #         console_user.insert_record({
                #             'resource': perm.desc,
                #             'resource_id': resource_id,
                #             'action': ACTION[req.method],
                #             'content': json.dumps(d, ensure_ascii=False,
                #                                   cls=EnhencedEncoder),
                #             'operator': req.user_id
                #         })
                #     console_user.insert_record(record)
        except Exception as e:
            _LOGGER.exception(e)
            return resp

        return resp


class ApiGateMiddleware(object):
    """GateMiddleware

    Gate,大门,所有API访问的进出口.
    该middleware应该放在middlewares的第一位

    该middleware做了以下事情:
    为每个request编号;
    拦截所有request进入, 把trace_id和ip,data等信息打印到log;
    拦截所有request出去, 把trace_id和此次访问所耗时间打印到log;
    """

    def process_request(self, request):
        try:
            track_logging.clear_trace()
            request.incoming_time = local_now()
            request.ip = get_client_ip(request)

            # 生成request.DATA
            request.DATA = None
            if request.method == 'GET':
                request.DATA = request.GET
            else:
                body = request.body
                if body:
                    if request.META.get('CONTENT_TYPE', '').startswith('application/json'):
                        request.DATA = json.loads(smart_unicode(body))
                    elif 'xml' in request.META.get('CONTENT_TYPE', ''):
                        request.DATA = minidom.parseString(smart_unicode(body))
                    else:
                        request.DATA = smart_unicode(body)
        except:
            _LOGGER.exception('middleware process request fail')

    def process_view(self, request, view_func, view_args, view_kwargs):
        """打印request进入时的log"""
        try:
            user = request.user and '%s(%s)' % (request.user.id, request.user.username)
            _LOGGER.info("%s - %s, USER: %s, DATA: %s, IP: %s" % (
                request.method.upper(), request.path, user, request.DATA, request.ip))
        except:
            _LOGGER.exception('middleware process view fail')

    def process_response(self, request, response):
        """拦截所有request出去, 把trace_id和此次访问所耗时间打印到log"""
        try:
            orm.session.close()
            if request.method.upper() == 'OPTIONS':
                return response

            duration = int((local_now() - request.incoming_time).total_seconds() * 1000)
            _LOGGER.info("URL: %s: %s, Duration:%s" % (request.method.upper(), request.path, duration))
            # 对慢请求做出错报警
            if duration >= 6000:
                _LOGGER.error("slow request, url: %s, duration: %s" % (request.path, duration))
        except:
            _LOGGER.exception('middleware process response fail')

        return response

    def process_exception(self, request, e):
        try:
            if isinstance(e, Error):
                _LOGGER.info('server error! %s %s %s', e.HTTPCODE,
                             unicode(e) or StatusCodeDict.get(e.STATUS),
                             request.path)
                return JsonResponse(
                    dict(status=e.STATUS,
                         msg=unicode(e) or StatusCodeDict.get(e.STATUS)),
                    status=e.HTTPCODE)
            else:
                """拦截所有未处理的exception,并把traceback转化为一行数据打印到log"""
                user_repr = "%s (%s)" % (request.user.id, request.user.username) if request.user else ''

                _LOGGER.error(
                    'REQUEST URL: %s, USER: %s, Unexpected Exception: %s' % (
                        request.path, user_repr, traceback.format_exc()))
        except:
            _LOGGER.exception('middleware process exception fail')
